﻿using Devil;
using GameToolkit;
using GameToolkit.Editor;
using System;
using System.Collections.Generic;
using Unity.AI.Navigation.Editor;
using UnityEditor;
using UnityEditor.AI;
using UnityEditorInternal;
using UnityEngine;

[CustomEditor(typeof(NavMeshRoadModifier)), CanEditMultipleObjects]
public class NavMeshRoadModifierEditor : Editor
{
    SerializedProperty m_AffectAgents;
    SerializedProperty m_IsRuntime;
    //int mEditIndex;
    int raycastLayer = 1;

    // 延申位置
    int mExpendIndex;
    GUIContent content = new GUIContent();
    public bool editingCollider
    {
        get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); }
    }

    int agentMask;
    Bounds bounds;

    string[] layers;

    Bounds GetBounds()
    {
        return bounds;
    }

    void Init()
    {
        var road = (NavMeshRoadModifier)target;
        var len = road.m_Points == null ? 0 : road.m_Points.Length;
        if (len < 2)
        {
            var arr = new Vector3[2];
            if (len > 0)
                arr[0] = road.m_Points[0];
            else
                arr[0] = Vector3.zero;
            arr[1] = Vector3.forward;
            road.m_Points = arr;
            UpdateBounds(road);
            EditorUtility.SetDirty(road);
        }
    }

    protected void OnEnable()
    {
        m_AffectAgents = serializedObject.FindProperty("m_AffectAgents");
        m_IsRuntime = serializedObject.FindProperty("m_IsRuntime");
        //mEditIndex = -1;
        mExpendIndex = -1;
        NavAgentTypeDrawer.GetAgentNames();
        agentMask = NavAgentTypeDrawer.GetAgentMask(m_AffectAgents);
#if !UNITY_2022_1_OR_NEWER
        NavMeshVisualizationSettings.showNavigation++;
#endif
        var lst = new List<string>();
        for (int i = 0; i < 32; i++)
        {
            var str = LayerMask.LayerToName(i);
            if (!string.IsNullOrEmpty(str))
                lst.Add(str);
        }
        layers = lst.ToArray();
        Init();
        var road = (NavMeshRoadModifier)target;
        //var toWorld = road.transform.localToWorldMatrix;
        bounds = VolumetricBounds.CalcuateBounds(road.transform.localToWorldMatrix, road.localBounds);
        NavMeshRoadModifier.selection = road;
    }

    private void OnDisable()
    {
#if !UNITY_2022_1_OR_NEWER
        NavMeshVisualizationSettings.showNavigation--;
#endif
        if (NavMeshRoadModifier.selection == target)
            NavMeshRoadModifier.selection = null;
    }

    public override void OnInspectorGUI()
    {
        if (!serializedObject.isEditingMultipleObjects)
            EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume",
                EditorGUIUtility.IconContent("EditCollider"), GetBounds, this);
        raycastLayer = EditorGUILayout.MaskField("Ground Layers", raycastLayer, layers);
        base.OnInspectorGUI();

        EditorGUI.BeginChangeCheck();
        EditorGUI.BeginDisabledGroup(Application.isPlaying);
        NavMeshComponentsGUIUtility.AgentMaskPopup("Affect Agents", m_AffectAgents);
        EditorGUILayout.PropertyField(m_IsRuntime);
        EditorGUI.EndDisabledGroup();
        if (EditorGUI.EndChangeCheck())
        {
            serializedObject.ApplyModifiedProperties();
        }
    }

    private void OnSceneGUI()
    {
        var roads = (NavMeshRoadModifier)target;
        if (!editingCollider)
        {
            //mEditIndex = -1;
            mExpendIndex = -1; 
            //bounds = VolumeBounds.CalcuateBounds(roads.transform.localToWorldMatrix, roads.m_LocalBounds);
            return;
        }
        //Init();
        var toWorld = roads.transform.localToWorldMatrix;
        var toLocal = roads.transform.worldToLocalMatrix;
        var len = roads.m_Points == null ? 0 : roads.m_Points.Length;
        Handles.color = Color.cyan;
        var cam = SceneView.currentDrawingSceneView.camera;
        var mpos = Event.current.mousePosition;
        mpos.y = cam.pixelHeight - mpos.y;
        var ray = cam.ScreenPointToRay(mpos);
        Vector3 pos = default, pos1 = default;
        RaycastHit hit = default;
        var raycast = Physics.Raycast(ray, out hit, 1000, raycastLayer, QueryTriggerInteraction.Ignore);
        if (mExpendIndex != -1 && !Event.current.control)
        {
            pos1 = ray.origin + ray.direction;
            if (raycast)
            {
                pos1 = hit.point;
            }
            Handles.DrawDottedLine(toWorld.MultiplyPoint(roads.m_Points[mExpendIndex]),
                pos1, 4);
            Handles.BeginGUI();
            if (!Event.current.isMouse || Event.current.button == 0)
            {
                if (GUI.Button(new Rect(new Vector2(mpos.x, cam.pixelHeight - mpos.y) + new Vector2(-8, -8), Vector2.one * 16), "", "OL Plus") && Event.current.button == 0)
                {
                    InsertAt(roads, mExpendIndex == 0 ? 0 : roads.m_Points.Length, toLocal.MultiplyPoint(pos1));
                    if (mExpendIndex != 0)
                        mExpendIndex = roads.m_Points.Length - 1;

                }
            }
            Handles.EndGUI();
            SceneView.currentDrawingSceneView.Repaint();
            return;
        }
        else
        {
            mExpendIndex = -1;
        }

        var snap = Vector3.one * 0.1f;

        int delIndex = -1;
        Vector3 delPos = Vector3.zero;
        int insertIndex = -1;
        float insertV = 0;
        for (int i = 0; i < len; i++)
        {
            pos = toWorld.MultiplyPoint(roads.m_Points[i]);
#if UNITY_2022_1_OR_NEWER
            if (i == 0)
                { var fmh_166_55_638857756257211127 = cam.transform.rotation; pos1 = Handles.FreeMoveHandle(i, pos, HandleUtility.GetHandleSize(pos) * 0.04f, snap, Handles.CircleHandleCap); }
            else
                { var fmh_168_55_638857756257219614 = Quaternion.identity; pos1 = Handles.FreeMoveHandle(i, pos, HandleUtility.GetHandleSize(pos) * 0.04f, snap, Handles.DotHandleCap); }
#else
            if (i == 0)
                pos1 = Handles.FreeMoveHandle(i, pos, cam.transform.rotation, HandleUtility.GetHandleSize(pos) * 0.04f, snap, Handles.CircleHandleCap);
            else
                pos1 = Handles.FreeMoveHandle(i, pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.04f, snap, Handles.DotHandleCap);
#endif
            if (MathExt.IsDifferent(pos, pos1, 0.01f))
            {
                Undo.RecordObject(roads, "Move Path Node");
                if (Event.current.control && raycast)
                {
                    pos1 = hit.point;
                }
                roads.m_Points[i] = toLocal.MultiplyPoint(pos1);
                UpdateBounds(roads);
                EditorUtility.SetDirty(roads);
            }

            Vector2 spos = cam.WorldToScreenPoint(pos1);
            if (Vector2.SqrMagnitude(spos - mpos) < 400)
            {
                delIndex = i;
                delPos = spos;
            }
            if (i == 0)
                continue;
            var pre = toWorld.MultiplyPoint(roads.m_Points[i - 1]);
            Vector2 spre = cam.WorldToScreenPoint(pre);
            var f = Vector2.Dot((spre - mpos).normalized, (spos - mpos).normalized);
            if (f < insertV)
            {
                insertV = f;
                insertIndex = i;
            }
        }

        // insert or delete or append
        if (delIndex != -1 || insertIndex != -1)
        {
            Handles.color = Color.yellow;
            pos = delIndex != -1 ? roads.m_Points[delIndex] : (ray.origin + ray.direction);
            if (Event.current.type == EventType.Repaint && delIndex == -1 && Event.current.control)
            {
                var p0 = insertIndex > 0 ? roads.m_Points[insertIndex - 1] : Vector3.zero;
                p0 = toWorld.MultiplyPoint(p0);
                var p1 = toWorld.MultiplyPoint(roads.m_Points[insertIndex]);
                Handles.DrawDottedLine(p0, pos, 4);
                Handles.DrawDottedLine(p1, pos, 4);
            }

            mpos.y = cam.pixelHeight - mpos.y;
            delPos.y = cam.pixelHeight - delPos.y;
            Handles.BeginGUI();
            //GUI.Label(new Rect(mpos - Vector2.up * 17, new Vector2(100, 20)), delIndex != -1 ? "[X] Delete" : "[I] Insert");
            if (delIndex != -1)
            {
                GUI.Label(new Rect(mpos + Vector2.down * 17, new Vector2(200, 20)), roads.m_Points[delIndex].ToString());
                if (len > 2 && GUI.Button(new Rect(delPos + new Vector3(-15, -20), new Vector2(15, 15)), "", "WinBtnCloseMac"))
                    DeleteAt(roads, delIndex);
                if ((delIndex == len - 1 || delIndex == 0) && GUI.Button(new Rect(delPos + new Vector3(1, -20), new Vector2(15, 15)), "", "WinBtnMaxMac"))
                    mExpendIndex = delIndex;
            }
            else if (insertIndex != -1 && Event.current.control && GUI.Button(new Rect(mpos + new Vector2(-8, -8), new Vector2(16, 16)), "", "WinBtnMaxMac"))
            {
                pos = roads.m_Points[insertIndex];
                pos1 = roads.m_Points[insertIndex - 1];
                var dir = (pos1 - pos).normalized;
                Vector3 newp = Vector3.Dot(toLocal.MultiplyPoint(ray.origin) - pos, dir) * dir + pos;
                if (raycast)
                    newp = toLocal.MultiplyPoint(hit.point);
                InsertAt(roads, insertIndex, newp);
            }
            if(Event.current.control && raycast)
            {
                var style = DevilEditorUtility.NormalStyle("warningoverlay");
                content.text = hit.collider.name;
                var size = style.CalcSize(content) + Vector2.one * 5;
                GUI.Label(new Rect(mpos + new Vector2(-size.x * 0.5f, -size.y - 5), size), content, style);
            }
            Handles.EndGUI();

            SceneView.currentDrawingSceneView.Repaint();
        }

    }

    void InsertAt(NavMeshRoadModifier roads, int insertIndex, Vector3 point)
    {
        var len = roads.m_Points.Length;
        Undo.RecordObject(roads, "Insert Path Node");
        var arr = new Vector3[len + 1];
        arr[insertIndex] = point;
        if (insertIndex > 0)
            Array.Copy(roads.m_Points, 0, arr, 0, insertIndex);
        if (insertIndex < len)
            Array.Copy(roads.m_Points, insertIndex, arr, insertIndex + 1, len - insertIndex);
        roads.m_Points = arr;
        UpdateBounds(roads);
        EditorUtility.SetDirty(roads);
    }

    void DeleteAt(NavMeshRoadModifier roads, int index)
    {
        Undo.RecordObject(roads, "Delete Path Node");
        var len = roads.m_Points.Length;
        var arr = new Vector3[len - 1];
        if (index > 0)
            Array.Copy(roads.m_Points, arr, index);
        if (index < len - 1)
            Array.Copy(roads.m_Points, index + 1, arr, index, arr.Length - index);
        roads.m_Points = arr;
        UpdateBounds(roads);
        EditorUtility.SetDirty(roads);
    }
    void UpdateBounds(NavMeshRoadModifier roads)
    {
        Bounds bd;
        var len = roads.m_Points == null ? 0 : roads.m_Points.Length;
        if (len == 0)
            bd = new Bounds(Vector3.zero, Vector3.one);
        else
            bd = new Bounds(roads.m_Points[0], Vector3.one);
        for (int i = 1; i < len; i++)
        {
            bd.Encapsulate(roads.m_Points[i]);
        }
        roads.m_LocalBounds = bd;
        //
    }
}
